import { useState, useEffect, useRef } from 'react'
import { debounce } from 'lodash-es'
import { getListByPage } from '../mock/index'
import './style/lazyList.css'
import { IList } from '../mock/index'

let itemHeight = 40

function VirtualList() {
	const listRef = useRef<HTMLDivElement | null>(null)
	const pageNumRef = useRef<number>(1)
	const listMapRef = useRef<Map<string, DOMRect>>(new Map())
	const [list, setList] = useState<IList[]>([])
	const [beginIndex, setBeginIndex] = useState<number>(0)
	const [endIndex, setEndIndex] = useState<number>(0)
	const [beginHeight, setBeginHeight] = useState<number>(0)
	const [endHeight, setEndHeight] = useState<number>(0)

	useEffect(() => {
		const list = getListByPage(pageNumRef.current)
		const offsetHeight = listRef.current?.offsetHeight
		const endIndex = offsetHeight
			? Math.ceil((offsetHeight * 2) / itemHeight)
			: 0
		const endHeight = (list.length - endIndex) * itemHeight
		setEndHeight(endHeight)
		setEndIndex(endIndex)
		setList(list)

		listRef.current?.addEventListener('scroll', handleScroll)
		return () => {
			listRef.current?.removeEventListener('scroll', handleScroll)
		}
	}, [])

	const cacheMapList = () => {
		console.log('list', list)
		const listDom = listRef.current
		if (listDom?.children) {
			for (let node of listDom.children) {
				const id = node.getAttribute('id')
				if (id?.includes('item')) {
					if (!listMapRef.current.has(id)) {
						listMapRef.current.set(id, node.getBoundingClientRect())
					}
				}
			}
		}
	}

	useEffect(cacheMapList, [list, beginIndex])

	// 滚动事件
	const handleScroll = debounce((e: Event) => {
		const target = e.target
		const { scrollTop, scrollHeight, offsetHeight } =
			target as EventTarget & {
				scrollTop: number
				scrollHeight: number
				offsetHeight: number
			}
		const [beginIndex, beginHeight] = getBeginIndex(scrollTop, offsetHeight)
		// 设置 endIndex 时，需要在 beginIndex 的基础上，加上 3 段屏幕的距离。（begin 隐藏区缓存数据、屏幕区、end 隐藏区缓存数据）
		const endIndex = beginIndex + Math.ceil((offsetHeight * 3) / itemHeight)
		const endHeight = scrollHeight - beginHeight - offsetHeight * 3
		setBeginHeight(beginHeight)
		setEndHeight(endHeight > 0 ? endHeight : 0)
		setBeginIndex(beginIndex)
		setEndIndex(endIndex)
	}, 300)

	const getBeginIndex = (scrollTop: number, offsetHeight: number) => {
		// 滚动的距离不超过屏幕的距离，默认从开始索引开始渲染。
		if (scrollTop < offsetHeight) {
			return [0, 0]
		}

		let height = 0
		let index = 0
		for (let value of listMapRef.current.values()) {
			height = height + value.height
			// 滚动的距离超过屏幕的距离，隐藏区域默认设置 1 屏幕的隐藏数据。
			// 优化点：这里可以将 计算好的相对高度，缓存起来，下次直接从缓存中拿
			if (height >= scrollTop - offsetHeight) {
				height = height - value.height
				break
			} else {
				index++
			}
		}
		// 动态计算平均 item 平均高度
		if (height > 0) {
			itemHeight = Math.ceil(height / index)
		}
		// console.log('itemHeight', itemHeight)
		return [index, height]
	}

	const getListRender = () => {
		const listData = list.slice(beginIndex, endIndex + 1)
		return (
			<>
				<div
					key="begin"
					style={{ height: beginHeight }}
					id="block-begin"
				></div>
				{listData.length
					? listData.map((item, index) => {
							return (
								<div
									key={item.id}
									className={
										index % 2 === 0 ? 'item' : 'item1'
									}
									id={`item-${item.id}`}
								>
									非定高虚拟滚动 - {item.value}
								</div>
							)
					  })
					: null}
				<div
					key="end"
					style={{ height: endHeight }}
					id="block-end"
				></div>
			</>
		)
	}
	return (
		<div className="lazy-list-container" ref={listRef}>
			{getListRender()}
		</div>
	)
}

export default VirtualList
